﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace Connection {

    /// <summary>
    /// Socket server class
    /// </summary>
    class SocketServer {

        /// <summary>
        /// Specification of port, on that will be communicated
        /// </summary>
        static int port = 5555;

        /// <summary>
        /// List of models saved on server.
        /// </summary>
        static List<ServerModelList> serverModelList = new List<ServerModelList>();

        /// <summary>
        /// Check if omc is running.
        /// </summary>
        /// <returns>True if omc is running, false otherwise</returns>
        static private Boolean OmcRunning() {
            if (Process.GetProcessesByName("omc").Length > 0) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Start new OMC process.
        /// </summary>
        static private void StartOmc() {
            // Get path to the OpenModelica root
            String omcPath = Environment.GetEnvironmentVariable("OPENMODELICAHOME");
            // Add path to OMC
            omcPath += "bin\\omc.exe";
            // Set path to OMC
            ProcessStartInfo newProcess = new ProcessStartInfo(omcPath);
            // Set OMC arguments
            newProcess.Arguments = "+d=interactiveCorba";
            // Run OMC
            Process.Start(newProcess);
        }

        /// <summary>
        /// Transform serverModelList to its string representation.
        /// </summary>
        /// <returns>List of models on server in string form.</returns>
        static private string ModelListToString() {
            String result = "";
            foreach (ServerModelList modelList in serverModelList) {
                result += modelList.FileName;
                result += "\n";
                result += modelList.RelativeDirectory;
                result += "\n";
            }
 //           Console.Write(result);
            return result;
        }

        /// <summary>
        /// Find all model files in a root directory, and all files within every nested directory.
        /// Results are stored in private variable.
        /// </summary>
        /// <param name="baseDir">The starting directory.</param>
        static private void GetAllModelFileNames(string baseDir) {
            
            // Clear List of Models
            serverModelList.Clear();    

            // Store a stack of our directories.
            Stack<string> directoryStack = new Stack<string>();
            directoryStack.Push(baseDir);

            // While there are directories to process
            while (directoryStack.Count > 0 ) {
                string currentDir = directoryStack.Pop();

                // Add all files at this directory.
                foreach (string fileName in Directory.GetFiles(currentDir, "*.mo")) {
                    ServerModelList modelFile = new ServerModelList();
                    modelFile.FileName = fileName.Substring(fileName.LastIndexOf("\\")+1);
                    modelFile.RelativeDirectory = currentDir.Substring(baseDir.Length);
                    if (modelFile.RelativeDirectory == "") modelFile.RelativeDirectory = "\\";
                    serverModelList.Add(modelFile);
                }

                // Add all directories at this directory.
                foreach (string directoryName in Directory.GetDirectories(currentDir)) {
                    directoryStack.Push(directoryName);
                }
            }
        }

        /// <summary>
        /// Stop current running omc by omc quit() directive
        /// </summary>
        static private void StopOmc() {
            try {
                OmcConnection.SendClass("quit()");
            }
            catch {
                // Omc terminated, no work is needed
            }
        }

        /// <summary>
        /// Main function implements server logic
        /// </summary>
        /// <param name="args">Command line arguments.</param>
        static void Main(string[] args) {
            StreamWriter fromOMC;          
            StreamReader toOMC;
            NetworkStream networkStream;
            TcpListener tcpListener = new TcpListener(port);    // I am listening on specific port
            String modelsRootPath;  // Path to Root of Modelica Models  
            
            // if Root inputed by command line
            if (args.Count() > 0) {
                modelsRootPath = args[0];
                if (modelsRootPath.EndsWith("\\")) modelsRootPath.Remove(modelsRootPath.Length-1);
            }
            else {  // implicit path to Root of Modelica Models
                modelsRootPath = Environment.GetEnvironmentVariable("OPENMODELICAHOME");
                modelsRootPath += "testmodels";
            }

            // Run OMC
            if (!OmcRunning()) {                
                Console.WriteLine("Omc is not running -> starting");
                StartOmc();
            }
            tcpListener.Start();
            Console.WriteLine("The Server has started on port " + port);
            try {
                while (true)    // Server main loop
                {
                    Socket serverSocket = tcpListener.AcceptSocket();
                    Console.WriteLine("Client connected");
                    networkStream = new NetworkStream(serverSocket);
                    fromOMC = new StreamWriter(networkStream);
                    toOMC = new StreamReader(networkStream);

                    String request;
                    String result;

                    try {
                        request = toOMC.ReadLine();  // Request to OMC
                    }
                    catch {
                        goto ClientDie;         // Request does not pass through -> terminate connection
                    }

                    Console.WriteLine("Client request: " + request);

                    switch (request) {
                        case "START_OMC":
                            if (!OmcRunning()) {                
                                Console.WriteLine("Starting omc by client request");
                                StartOmc();
                            }
                            goto NormalEnd;
//                            break;
                        case "STOP_OMC":
                            if (OmcRunning()) {                
                                Console.WriteLine("Stopping omc by client request");
                                StopOmc();
                            }
                            goto NormalEnd;
//                            break;
                        case "MODEL_LIST":
                            GetAllModelFileNames(modelsRootPath);
                            result = ModelListToString();
                            goto FlushResult;
//                            break;
                        default:
                            if (!OmcRunning()) {    // this is not needed, may be deleted in future
                                Console.WriteLine("Starting omc because of omc client request");
                                StartOmc();
                            }
                            break;
                    }

                    if (request.StartsWith("LOAD")) {
                        string[] words = request.Split('\"');
                        String path = modelsRootPath + words[2] + words[1];
                        path = path.Replace('\\', '/');
                        request = "loadFile(\"" + path +"\")";
                    }
                    else if (request.StartsWith("SAVE_TO_FILE")) {
                        string[] words = request.Split('\"');
                        String filePath = modelsRootPath + words[3] + words[2];
                        filePath = filePath.Replace('\\', '/');
                        String className = words[1];
                        request = "setSourceFile(" + className + ",\"" + filePath + "\")";
                    }

                    try {
                        result = OmcConnection.SendClass(request);  // Sending request to OMC and query a result
                    }
                    catch {
                        result = "Connection error";        // if any error, returned error
                    }

                    Console.WriteLine("Result: " + result);

                FlushResult:
                    try {
                        fromOMC.WriteLine(result);
                        fromOMC.Flush();
                    }
                    catch {
                        goto ClientDie;
                    }
                    goto NormalEnd;

                ClientDie:
                    Console.WriteLine("Cleint died, connection is terminated");

                NormalEnd:
                    serverSocket.Close();                   // terminating current connection
                    Console.WriteLine("Client disconected");
                }
                Console.Read();
            }
            catch (SocketException ex) {
                Console.WriteLine(ex);
                return;
            }
            catch (Exception ex) {
                Console.WriteLine(ex);
            }
        }
    }
}